package com.kexio.enterprise.observability.logging.aspect;

import com.kexio.enterprise.observability.logging.LogService;
import com.kexio.enterprise.observability.logging.annotation.OperateLog;
import com.kexio.enterprise.observability.logging.domain.OperateLogEntry;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import jakarta.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.UUID;

/**
 * 操作日志AOP切面
 * 
 * 拦截标注了@OperateLog注解的方法，自动记录操作日志
 * 
 * @author Kexio
 * @since 1.0.0
 */
@Aspect
@Component
@Order(100) // 较低优先级，确保在其他切面之后执行
public class LogAspect {

    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);

    private final LogService logService;
    private final ObjectMapper objectMapper;
    private final ExpressionParser expressionParser;

    public LogAspect(LogService logService, ObjectMapper objectMapper) {
        this.logService = logService;
        this.objectMapper = objectMapper;
        this.expressionParser = new SpelExpressionParser();
    }

    /**
     * @OperateLog注解切点
     */
    @Pointcut("@annotation(com.kexio.enterprise.observability.logging.annotation.OperateLog)")
    public void operateLogPointcut() {
    }

    /**
     * 操作日志环绕通知
     */
    @Around("operateLogPointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        // 获取方法签名和注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        OperateLog operateLog = AnnotationUtils.getAnnotation(method, OperateLog.class);
        
        if (operateLog == null) {
            return point.proceed();
        }

        // 检查条件表达式
        if (!checkCondition(operateLog, point)) {
            return point.proceed();
        }

        // 创建日志条目
        OperateLogEntry logEntry = new OperateLogEntry();
        
        // 记录开始时间
        long startTime = System.currentTimeMillis();
        
        // 填充基本信息
        fillBasicInfo(logEntry, operateLog, point, method);
        
        // 记录请求信息
        fillRequestInfo(logEntry, operateLog);
        
        // 设置追踪信息
        setTraceInfo(logEntry);

        Object result = null;
        Throwable exception = null;
        boolean success = true;

        try {
            // 执行目标方法
            result = point.proceed();
            
            // 记录返回结果
            if (operateLog.includeResult() && result != null) {
                try {
                    String resultJson = objectMapper.writeValueAsString(result);
                    logEntry.setResponseResult(truncateString(resultJson, 2000));
                } catch (Exception e) {
                    logEntry.setResponseResult("序列化结果失败: " + e.getMessage());
                }
            }
            
        } catch (Throwable e) {
            exception = e;
            success = false;
            
            // 记录异常信息
            if (operateLog.includeException()) {
                logEntry.setExceptionMessage(e.getMessage());
                logEntry.setExceptionMessage(getStackTrace(e));
            }
            
            throw e;
        } finally {
            // 计算执行时间
            long endTime = System.currentTimeMillis();
            long executionTime = endTime - startTime;
            
            if (operateLog.includeExecutionTime()) {
                logEntry.setExecutionTime(executionTime);
            }
            
            logEntry.setSuccess(success);
            logEntry.setCreateTime(LocalDateTime.now());
            
            // 解析详情表达式
            String detail = parseDetail(operateLog.detail(), point, result, exception);
            logEntry.setDetail(detail);
            
            // 决定是否记录日志
            boolean shouldLog = (success && operateLog.logOnSuccess()) || 
                              (!success && operateLog.logOnFailure());
            
            if (shouldLog) {
                // 记录日志
                if (operateLog.async()) {
                    logService.logAsync(logEntry);
                } else {
                    logService.log(logEntry);
                }
                
                // 同时记录到SLF4J
                logToSlf4j(operateLog, logEntry, success);
            }
        }

        return result;
    }

    /**
     * 检查条件表达式
     */
    private boolean checkCondition(OperateLog operateLog, ProceedingJoinPoint point) {
        String condition = operateLog.condition();
        if (!StringUtils.hasText(condition)) {
            return true;
        }
        
        try {
            EvaluationContext context = createEvaluationContext(point, null, null);
            Expression expression = expressionParser.parseExpression(condition);
            Object result = expression.getValue(context);
            return Boolean.TRUE.equals(result);
        } catch (Exception e) {
            log.warn("条件表达式解析失败: {}", condition, e);
            return true; // 解析失败时默认记录日志
        }
    }

    /**
     * 填充基本信息
     */
    private void fillBasicInfo(OperateLogEntry logEntry, OperateLog operateLog, 
                              ProceedingJoinPoint point, Method method) {
        logEntry.setModule(operateLog.module());
        logEntry.setOperation(operateLog.operation());
        logEntry.setType(operateLog.type());
        logEntry.setLevel(operateLog.level());
        logEntry.setBusinessKey(operateLog.businessKey());
        logEntry.setTags(operateLog.tags());

        // 记录请求参数
        if (operateLog.includeArgs()) {
            try {
                Object[] args = point.getArgs();
                if (args != null && args.length > 0) {
                    String argsJson = objectMapper.writeValueAsString(args);
                    logEntry.setRequestParams(truncateString(argsJson, 2000));
                }
            } catch (Exception e) {
                logEntry.setRequestParams("序列化参数失败: " + e.getMessage());
            }
        }

        // 获取用户信息
        fillUserInfo(logEntry);
    }

    /**
     * 填充请求信息
     */
    private void fillRequestInfo(OperateLogEntry logEntry, OperateLog operateLog) {
        try {
            ServletRequestAttributes attributes = 
                (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                logEntry.setRequestUrl(request.getRequestURL().toString());
                logEntry.setRequestMethod(request.getMethod());
                logEntry.setRequestIp(getClientIpAddress(request));
                logEntry.setUserAgent(request.getHeader("User-Agent"));
            }
        } catch (Exception e) {
            log.debug("获取请求信息失败", e);
        }
    }

    /**
     * 设置追踪信息
     */
    private void setTraceInfo(OperateLogEntry logEntry) {
        // TODO: 集成分布式追踪系统
        // 这里简化处理，生成一个追踪ID
        String traceId = UUID.randomUUID().toString().replace("-", "");
        logEntry.setTraceId(traceId);
        logEntry.setSpanId(traceId.substring(0, 16));
    }

    /**
     * 填充用户信息
     */
    private void fillUserInfo(OperateLogEntry logEntry) {
        try {
            // TODO: 从用户上下文获取用户信息
            // Long userId = UserInfoUtils.getCurrentUserId();
            // String username = UserInfoUtils.getCurrentUsername();
            // String realName = UserInfoUtils.getCurrentUserRealName();
            // Long tenantId = UserInfoUtils.getCurrentTenantId();
            // String tenantName = UserInfoUtils.getCurrentTenantName();
            
            // logEntry.setUserId(userId);
            // logEntry.setUsername(username);
            // logEntry.setUserRealName(realName);
            // logEntry.setTenantId(tenantId);
            // logEntry.setTenantName(tenantName);
            
            // 暂时使用默认值
            logEntry.setUserId(1L);
            logEntry.setUsername("system");
            logEntry.setUserRealName("系统用户");
            logEntry.setTenantId(1L);
            logEntry.setTenantName("默认租户");
            
        } catch (Exception e) {
            log.debug("获取用户信息失败", e);
        }
    }

    /**
     * 解析详情表达式
     */
    private String parseDetail(String detailTemplate, ProceedingJoinPoint point, 
                             Object result, Throwable exception) {
        if (!StringUtils.hasText(detailTemplate)) {
            return "";
        }
        
        try {
            EvaluationContext context = createEvaluationContext(point, result, exception);
            Expression expression = expressionParser.parseExpression(detailTemplate);
            Object value = expression.getValue(context);
            return value != null ? value.toString() : "";
        } catch (Exception e) {
            log.warn("详情表达式解析失败: {}", detailTemplate, e);
            return detailTemplate; // 解析失败时返回原始模板
        }
    }

    /**
     * 创建SpEL评估上下文
     */
    private EvaluationContext createEvaluationContext(ProceedingJoinPoint point, 
                                                    Object result, Throwable exception) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        
        // 添加方法参数
        Object[] args = point.getArgs();
        String[] paramNames = ((MethodSignature) point.getSignature()).getParameterNames();
        
        if (args != null && paramNames != null) {
            for (int i = 0; i < Math.min(args.length, paramNames.length); i++) {
                context.setVariable(paramNames[i], args[i]);
                context.setVariable("arg" + i, args[i]);
            }
        }
        
        // 添加返回结果
        if (result != null) {
            context.setVariable("result", result);
        }
        
        // 添加异常信息
        if (exception != null) {
            context.setVariable("exception", exception);
            context.setVariable("errorMessage", exception.getMessage());
        }
        
        // 添加常用变量
        context.setVariable("now", LocalDateTime.now());
        context.setVariable("timestamp", System.currentTimeMillis());
        
        return context;
    }

    /**
     * 记录到SLF4J日志
     */
    private void logToSlf4j(OperateLog operateLog, OperateLogEntry logEntry, boolean success) {
        String message = String.format("[%s] %s.%s - %s", 
                success ? "SUCCESS" : "FAILURE",
                logEntry.getModule(), 
                logEntry.getOperation(),
                logEntry.getDetail());
        
        switch (operateLog.level()) {
            case TRACE:
                log.trace(message);
                break;
            case DEBUG:
                log.debug(message);
                break;
            case INFO:
                log.info(message);
                break;
            case WARN:
                log.warn(message);
                break;
            case ERROR:
                log.error(message);
                break;
        }
    }

    /**
     * 获取客户端IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        
        // 处理多个IP的情况
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        
        return ip;
    }

    /**
     * 获取异常堆栈跟踪
     */
    private String getStackTrace(Throwable throwable) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        throwable.printStackTrace(pw);
        return truncateString(sw.toString(), 4000);
    }

    /**
     * 截断字符串
     */
    private String truncateString(String str, int maxLength) {
        if (str == null || str.length() <= maxLength) {
            return str;
        }
        return str.substring(0, maxLength) + "...";
    }
}
